<?php

namespace App\Helpers;

/**
 * 微信支付公共方法
 *
 * @Desc:
 *
 * @author: zhx
 *
 * @Time: 2023/8/4 13:58
 */
class BasicWePay
{
    /**
     * 接口基础地址
     *
     * @var string
     */
    protected $base = 'https://api.mch.weixin.qq.com';

    /**
     * 配置参数
     *
     * @var array
     */
    protected $config = [
        'appid' => '', // 微信绑定APPID，需配置
        'mch_id' => '', // 微信商户编号，需要配置
        'mch_v3_key' => '', // 微信商户密钥，需要配置
        'cert_serial' => '', // 商户证书序号，无需配置
        'cert_public' => '', // 商户公钥内容，需要配置
        'cert_private' => '', // 商户密钥内容，需要配置

        'mp_cert_serial' => '', // 平台证书序号，无需配置
        'mp_cert_content' => '', // 平台证书内容，无需配置
    ];

    /**
     * V3支付类型
     *
     * @var string[]
     */
    protected $V3_PAY_TYPES = [
        'h5' => '/v3/pay/transactions/h5',
        'app' => '/v3/pay/transactions/app',
        'jsapi' => '/v3/pay/transactions/jsapi',
        'native' => '/v3/pay/transactions/native',
    ];

    /**
     * V3 微信支付订单号查询
     *
     * @var string
     */
    protected $ORDER_INFO = '/v3/pay/transactions/id/';

    /**
     * V3 关闭订单API
     *
     * @var string
     */
    protected $ORDER_CLOSE = '/v3/pay/transactions/out-trade-no/"$s"/close';

    /**
     * V3 申请退款API
     *
     * @var string
     */
    protected $ORDER_REFUND = '/v3/refund/domestic/refunds';

    /**
     * BasicWePayV3 constructor.
     *
     * @param  array  $options [mch_id, mch_v3_key, cert_public, cert_private]
     *
     * @throws \WeChat\Exceptions\InvalidResponseException
     * @throws \WeChat\Exceptions\LocalCacheException
     */
    public function __construct(array $options = [])
    {
        if (empty($options['mch_id'])) {
            throw new \InvalidArgumentException('Missing Config -- [mch_id]');
        }
        if (empty($options['mch_v3_key'])) {
            throw new \InvalidArgumentException('Missing Config -- [mch_v3_key]');
        }
        if (empty($options['cert_public'])) {
            throw new \InvalidArgumentException('Missing Config -- [cert_public]');
        }
        if (empty($options['cert_private'])) {
            throw new \InvalidArgumentException('Missing Config -- [cert_private]');
        }

        if (stripos($options['cert_public'], '-----BEGIN CERTIFICATE-----') === false) {
            if (file_exists($options['cert_public'])) {
                $options['cert_public'] = file_get_contents($options['cert_public']);
            } else {
                throw new \InvalidArgumentException('File Non-Existent -- [cert_public]');
            }
        }

        if (stripos($options['cert_private'], '-----BEGIN PRIVATE KEY-----') === false) {
            if (file_exists($options['cert_private'])) {
                $options['cert_private'] = file_get_contents($options['cert_private']);
            } else {
                throw new \nvalidArgumentException('File Non-Existent -- [cert_private]');
            }
        }

        $this->config['appid'] = isset($options['appid']) ? $options['appid'] : '';
        $this->config['mch_id'] = $options['mch_id'];
        $this->config['mch_v3_key'] = $options['mch_v3_key'];
        $this->config['cert_public'] = $options['cert_public'];
        $this->config['cert_private'] = $options['cert_private'];
        if (empty($options['cert_serial'])) {
            $this->config['cert_serial'] = openssl_x509_parse($this->config['cert_public'], true)['serialNumberHex'];
        } else {
            $this->config['cert_serial'] = $options['cert_serial'];
        }
        if (empty($this->config['cert_serial'])) {
            throw new \InvalidArgumentException('Failed to parse certificate public key');
        }

    }

    /**
     * 生成微信支付 v3 请求的授权头（Authorization Header）
     *
     * @Desc: 在使用微信支付 v3 API 进行请求时，需要在请求头中包含特定格式的授权信息，以确保请求的合法性和安全性。
     *
     * @return string
     *
     * @author: zhx
     *
     * @Time: 2023/8/4 15:32
     */
    protected function generateAuthorizationHeader($httpMethod, $url)
    {
        $urlParts = parse_url($url);
        $canonicalUrl = ($urlParts['path'].(! empty($urlParts['query']) ? "?${urlParts['query']}" : ''));
        $timestamp = time();
        $nonce = $this->createNoncestr(); // 随机字符串

        $message = "${httpMethod}\n${canonicalUrl}\n${timestamp}\n${nonce}\n";
        openssl_sign($message, $rawSign, $this->mchPrivateKey, 'sha256WithRSAEncryption');
        $sign = base64_encode($rawSign);

        $schema = 'WECHATPAY2-SHA256-RSA2048';
        $token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"', $this->mchId, $nonce, $timestamp, $this->certSerialNo, $sign);

        return "${schema} ${token}";
    }

    /**
     * 生成数据签名
     *
     * @param  string  $data 签名内容
     * @return string
     */
    protected function signBuild($data)
    {
        $pkeyid = openssl_pkey_get_private($this->config['cert_private']);
        openssl_sign($data, $signature, $pkeyid, 'sha256WithRSAEncryption');

        return base64_encode($signature);
    }

    /**
     * 产生随机字符串
     *
     * @param  int  $length 指定字符长度
     * @param  string  $str 字符串前缀
     * @return string
     */
    public function createNoncestr($length = 32, $str = '')
    {
        $chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }

        return $str;
    }
}
